package top.hcode.hoj.service.admin;

import cn.hutool.core.lang.Validator;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.nutz.lang.Lang;
import org.nutz.lang.Strings;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;
import top.hcode.hoj.common.LazyDao;
import top.hcode.hoj.dao.OrgClassDao;
import top.hcode.hoj.dao.org.OrgDao;
import top.hcode.hoj.pojo.dto.org.QueryClassDTO;
import top.hcode.hoj.pojo.dto.org.QueryGradeDTO;
import top.hcode.hoj.pojo.entity.org.Org;
import top.hcode.hoj.pojo.entity.org.OrgClass;
import top.hcode.hoj.pojo.form.AdmQueryOrgForm;
import top.hcode.hoj.pojo.vo.org.ClassVO;
import top.hcode.hoj.pojo.vo.org.GradeVO;
import top.hcode.hoj.pojo.vo.org.SchoolVO;
import top.hcode.hoj.utils.Constants;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * @author orangej
 * @since 2025-01-20
 */
@Slf4j
@Component
@RequiredArgsConstructor
public class AdminOrgService {
    private final OrgDao orgDao;
    private final OrgClassDao orgClassDao;
    private final LazyDao dao;

    public IPage<Org> getOrgList(AdmQueryOrgForm form) {
        IPage<Org> pager = new Page<>(form.getCurrentPage(), form.getLimit());

        QueryWrapper<Org> query = new QueryWrapper<>();
        query.like(Strings.isNotBlank(form.getKeyword()), "org_name", form.getKeyword())
                .eq(Strings.isNotBlank(form.getParentId()), "parent_id", form.getParentId())
                .eq(form.getOrgType() != null, "org_type", form.getOrgType())
                .like(Strings.isNotBlank(form.getTreeCode()), "tree_code", form.getTreeCode())
                .orderByAsc("tree_code");

        orgDao.page(pager, query);
        return pager;
    }

    public Org getOrg(Long id) {
        return orgDao.getById(id);
    }

    @Transactional
    public Org saveOrg(Org org) {
        // 更新
        boolean isUpdate = Strings.isNotBlank(org.getId());
        if (isUpdate) {
            orgDao.updateById(org);
            return org;
        }

        // 新建
        Validator.validateNotEmpty(org.getOrgName(), "组织名称不能为空");
        Validator.validateNotNull(org.getOrgType(), "组织类型不能为空");
        Constants.OrgType type = Constants.OrgType.getOrgType(org.getOrgType());
        Validator.validateNotNull(type, "组织类型不存在 : {}", org.getOrgType());

        if (Constants.OrgType.SCHOOL.equals(type)) {
            // 新建学校

            // 自动生成 treeCode
            if (Strings.isBlank(org.getTreeCode())) {
                String maxTreeCode = orgDao.queryChildMaxTreeCode(null);
                String treeCode = String.format("%04d", Integer.parseInt(StrUtil.subSufByLength(maxTreeCode, 4)) + 1);
                org.setTreeCode(treeCode);
            }

            Validator.validateTrue(org.getTreeCode().trim().length() == 4, "树编码长度错误");
            Validator.validateTrue(org.getTreeCode().compareTo("1000") > 0, "树编码必须大于1000");
            Validator.validateTrue(orgDao.countByTreeCode(org.getTreeCode()) == 0, "树编码已存在 : {}", org.getTreeCode());

            org.setParentId(null);
            orgDao.save(org);

            // create campus
            Org campus = new Org();
            campus.setParentId(org.getId());
            campus.setOrgName(org.getOrgName() + "校区");
            campus.setOrgType(Constants.OrgType.CAMPUS.getType());
            createEduOrg(campus, Constants.OrgType.CAMPUS);

            // create semester
            Org semester = new Org();
            semester.setParentId(campus.getId());
            semester.setOrgName("小学");
            semester.setOrgType(Constants.OrgType.SEMESTER.getType());
            createEduOrg(semester, Constants.OrgType.SEMESTER);
        } else {
            // 新建子节点
            createEduOrg(org, type);
        }
        return org;
    }

    private void createEduOrg(Org org, Constants.OrgType type) {
        Validator.validateNotNull(type, "组织类型不能为空");
        Validator.validateNotNull(org.getParentId(), "上级节点不能为空");
        org.setOrgType(type.getType());

        Org parent = orgDao.getById(org.getParentId());
        Validator.validateNotNull(parent, "上级节点不存在 : {}", org.getParentId());
        Constants.OrgType parentOrgType = Constants.OrgType.getOrgType(parent.getOrgType());
        Validator.validateTrue(isValidChildOrgType(parentOrgType, type),
                "{}下无法添加 : {}", parentOrgType.getLabel(), type.getLabel());

        // check grade and class num
        if (type.equals(Constants.OrgType.GRADE)) {
            Validator.validateNotNull(org.getGradeNum(), "年级不能为空");
        } else if (type.equals(Constants.OrgType.CLASS)) {
            Validator.validateNotNull(org.getGradeNum(), "年级不能为空");
            Validator.validateNotNull(org.getClassNum(), "班级不能为空");
        }

        // 自动生成 treeCode
        if (Strings.isBlank(org.getTreeCode())) {
            String maxTreeCode = orgDao.queryChildMaxTreeCode(parent.getId());
            if (Strings.isBlank(maxTreeCode)) {
                maxTreeCode = parent.getTreeCode() + "0000";
            }

            String treeCode = parent.getTreeCode() + String.format("%04d", Integer.parseInt(StrUtil.subSufByLength(maxTreeCode, 4)) + 1);
            org.setTreeCode(treeCode);
        }

        orgDao.save(org);

        if (type.equals(Constants.OrgType.CLASS)) {
            OrgClass orgClass = new OrgClass();
            orgClass.setId(org.getId());
            orgClass.setClassName(org.getOrgName());
            orgClass.setGradeNum(org.getGradeNum());
            orgClass.setClassNum(org.getClassNum());
            orgClass.setGradeId(parent.getId());
            orgClass.setTreeCode(parent.getTreeCode());

            String code = org.getTreeCode();
            String campusCode = StrUtil.sub(code, 0, 8);
            Org campus = orgDao.getByTreeCode(campusCode);
            if (campus != null) {
                orgClass.setCampusId(campus.getId());
                orgClass.setSchoolId(campus.getParentId());
            }

            orgClassDao.save(orgClass);
        }
    }

    public void deleteOrg(Long id) {
        if (orgDao.count(new QueryWrapper<Org>().eq("parent_id", id)) > 0) {
            throw new IllegalArgumentException("该组织存在下级，无法删除");
        }

        orgDao.removeById(id);
    }

    public List<GradeVO> getGradeList(QueryGradeDTO queryGradeDTO) {
        Org school = orgDao.getById(queryGradeDTO.getSchoolId());
        Validator.validateNotNull(school, "学校不存在 : {}", queryGradeDTO.getSchoolId());
        List<Org> orgList = orgDao.queryChildrenByTreeCode(school.getTreeCode(), Constants.OrgType.GRADE);

        return orgList.stream()
                .map(org -> {
                    GradeVO gradeVO = new GradeVO();
                    gradeVO.setId(org.getId());
                    gradeVO.setGradeNum(org.getGradeNum());
                    gradeVO.setName(org.getOrgName());
                    return gradeVO;
                })
                .collect(Collectors.toList());
    }

    public List<ClassVO> getClassList(QueryClassDTO queryClassDTO) {
        Org parent = null;
        if (Strings.isNotBlank(queryClassDTO.getSchoolId())) {
            parent = orgDao.getById(queryClassDTO.getSchoolId());
        }
        if (Strings.isNotBlank(queryClassDTO.getGradeId())) {
            parent = orgDao.getById(queryClassDTO.getGradeId());
        }

        Validator.validateNotNull(parent, "学校或年级不存在 : {}, {}", queryClassDTO.getSchoolId(), queryClassDTO.getGradeId());
        assert parent != null;
        List<Org> orgList = orgDao.queryChildrenByTreeCode(parent.getTreeCode(), Constants.OrgType.CLASS);

        return orgList.stream()
                .map(org -> {
                    ClassVO classVO = new ClassVO();
                    classVO.setId(org.getId());
                    classVO.setName(org.getOrgName());
                    classVO.setClassNum(org.getClassNum());
                    classVO.setGradeNum(org.getGradeNum());
                    return classVO;
                })
                .collect(Collectors.toList());
    }


    public List<SchoolVO> getCampusVOList(List<String> schoolIdList) {
        List<String> treeCodeList = orgDao.queryTreeCodeList(schoolIdList);
        if (Lang.isEmpty(treeCodeList)) {
            return new ArrayList<>();
        }

        Map<String, Org> shoolMap = orgDao.queryAllSchoolMap();
        List<Org> orgList = orgDao.queryChildrenByTreeCode(treeCodeList, Constants.OrgType.CAMPUS);
        return orgList.stream().map(org -> {
                    SchoolVO vo = new SchoolVO().setName(org.getOrgName()).setId(org.getId());

                    // 添加学校名称
                    String parentId = org.getParentId();
                    Org parent = shoolMap.get(parentId);
                    if (parent != null && !parent.getOrgName().equals(vo.getName())) {
                        vo.setName(parent.getOrgName() + " - " + vo.getName());
                    }
                    return vo;
                })
                .collect(Collectors.toList());
    }

    public List<SchoolVO> getTopSchoolVOList(List<String> schoolIdList) {
        if (Lang.isEmpty(schoolIdList)) {
            return new ArrayList<>();
        }
        return orgDao.lambdaQuery().in(Org::getId, schoolIdList)
                .list()
                .stream()
                .map(org -> new SchoolVO().setName(org.getOrgName()).setId(org.getId()))
                .collect(Collectors.toList());
    }

    public boolean isValidChildOrgType(Constants.OrgType parentOrgType, Constants.OrgType childOrgType) {
        Validator.validateNotNull(parentOrgType, "上级组织类型不能为空");
        Validator.validateNotNull(childOrgType, "下级组织类型不能为空");
        Integer type0 = parentOrgType.getType();
        Integer type1 = childOrgType.getType();
        return type0.equals(type1 - 1) || (type0.equals(3) && type1.equals(5));
    }

    public List<SchoolVO> getCampusVOListByUser(String uid) {
        return dao.selectList(SchoolVO.class,
                "select id, org_name as name from org where id in " +
                "(select org_id from org_has_user where user_id = ? and org_type = 3) order by tree_code", uid);
    }

    public void createGrade(String schoolName, Integer gradeNum) {
        Org school = orgDao.getSchoolByName(schoolName);
        Validator.validateNotNull(school, "学校不存在 : {}", schoolName);

        Org parent = orgDao.getFirstSemester(school.getTreeCode());
        Validator.validateNotNull(parent, "学校没有学段 : {}", schoolName);

        Org org = new Org();
        org.setOrgName(Constants.GradeNum.getGradeClass(gradeNum).getLabel());
        org.setOrgType(Constants.OrgType.GRADE.getType());
        org.setParentId(parent.getId());
        org.setGradeNum(gradeNum);
        saveOrg(org);
    }

    public void createClass(String schoolName, Integer gradeNum, Integer classNum) {
        Org school = orgDao.getSchoolByName(schoolName);
        Validator.validateNotNull(school, "学校不存在 : {}", schoolName);

        Org grade = orgDao.getGradeBySchoolTreeCodeAndGradeNum(school.getTreeCode(), gradeNum);
        Validator.validateNotNull(grade, "学校没有年级 : {}", schoolName);

        String gradeName = Constants.GradeNum.getGradeClass(gradeNum).getLabel();
        String className = gradeName + classNum + "班";

        Org org = new Org();
        org.setOrgName(className);
        org.setOrgType(Constants.OrgType.CLASS.getType());
        org.setParentId(grade.getId());
        org.setClassNum(classNum);
        org.setGradeNum(gradeNum);
        saveOrg(org);
    }

    public List<Org> getAncestorOrgList(String orgId) {
        Org org = orgDao.getById(orgId);
        if (org == null) {
            return new ArrayList<>();
        }

        // 将 treeCode 按 4 位分割
        String treeCode = org.getTreeCode();
        List<String> codeList = new ArrayList<>();
        for (int i = 4; i < treeCode.length(); i += 4) {
            codeList.add(treeCode.substring(0, i + 4));
        }
        if (codeList.isEmpty()) {
            return new ArrayList<>();
        }

        return dao.selectList(Org.class, "select * from org where tree_code in (?) order by tree_code"
                , codeList);
    }
}
